﻿using CommandSystem;
using Exiled.API.Features;
using Exiled.API.Features.Components;
using MEC;
using Mirror;
using RemoteAdmin;
using RgCore.Database;
using RgCore.GameData;
using RgCore.Menu;
using RgCore.Misc;
using RgCore.Modules;
using RgCore.PlayerHud;
using SCPSLAudioApi.AudioCore;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using UnityEngine;
using Utils;
using VoiceChat;
using static InventoryStuff.Items;

namespace SponsorSupport
{
    public class AnnounceCommand : ICommand
    {
        public string Command => "bc";

        public string[] Aliases => Array.Empty<string>();

        public string Description => "";

        private int GetBCard(Player player)
        {
            var stuff = InventoryStuff.InventoryHandler.AllStuffs[player.UserId].FirstOrDefault(x => x.Key is BroadcastCard);
            return stuff.Key == null ? 0 : stuff.Value;
        }

        private void UseBCard(Player player)
        {
            var stuff = InventoryStuff.InventoryHandler.AllStuffs[player.UserId].FirstOrDefault(x => x.Key is BroadcastCard);
            InventoryStuff.InventoryHandler.AllStuffs[player.UserId][stuff.Key]--;
            InventoryStuff.InventoryHandler.GameplayData_OnUploadingData(player);
        }

        public bool Execute(ArraySegment<string> arguments, ICommandSender sender, out string response)
        {
            Player player = Player.Get(sender);
            bool flag = SponsorHandler.Rainbow.ContainsKey(player) && SponsorHandler.Announce.Contains(player);
            if (flag)
            {
                SponsorHandler.Announce.Remove(player);
            }
            else if (ModuleManager.IsLoaded("inventory_plugin"))
            {
                if (GetBCard(player) > 0)
                {
                    flag = true;
                    UseBCard(player);
                }
            }
            if (!flag)
            {
                response = "你没有彩虹称号/彩虹用过了/公告卡";
                return false;
            }
            string text = RAUtils.FormatArguments(arguments, 0);
            if (Encoding.UTF8.GetByteCount(text) > 384)
            {
                response = "你的输入内容过长！（要求限制在384字节内，汉字占3字符，符号占2字符，字母数字占1字符）";
                return false;
            }
            HudHandler.SendFormattedMsg("", "<color=#FF69B4>全服公告</color>", $"<color=#00FF00>{player.Nickname}</color>: {text.Replace('<', '[').Replace('>', ']')}", 5, MsgType.Announcement, ScreenLocation.CenterTop, true, Player.List.ToArray());
            response = "已发送";
            return true;
        }
    }

    public sealed class SponsorConfig : IConfig
    {
        [Description("预留位数量")]
        public int ReservedSlot { get; set; } = 1;

        [Description("启用管理专用预留位（1位）")]
        public bool AdminReservationEnabled { get; set; } = true;
    }

    public class SponsorPlugin : RgPlugin
    {
        public override string Name => "赞助者项目";

        public override string Id => "sponsor_support";
        public override object Config { get; set; } = new SponsorConfig();
        public static SponsorPlugin instance = null;
        public override Exiled.API.Enums.PluginPriority Priority => Exiled.API.Enums.PluginPriority.Lower;

        public override void OnDisabled()
        {
            instance = null;
            if (DatabaseController.DatabaseDisabled)
            {
                return;
            }
            SponsorHandler.SetEnabled(false);
            QueryProcessor.DotCommandHandler.UnregisterCommand(QueryProcessor.DotCommandHandler.AllCommands.FirstOrDefault(x => x is AnnounceCommand));
        }

        public override void OnEnabled()
        {
            instance = this;
            if (DatabaseController.DatabaseDisabled)
            {
                Log.Warn("由于数据库未启用，此插件不加载");
                return;
            }
            SCPSLAudioApi.Startup.SetupDependencies();
            SponsorHandler.SetEnabled(true);
            QueryProcessor.DotCommandHandler.RegisterCommand(new AnnounceCommand());
        }
    }

    public readonly struct Badge
    {
        public Badge(string name, string color, string description, DateTime expires)
        {
            Name = name;
            Color = color;
            Description = description;
            Expires = expires;
        }

        public readonly string Name;
        public readonly string Color;
        public readonly string Description;
        public readonly DateTime Expires;
    }

    public static class SponsorHandler
    {
        public static List<string> Reservalist = new List<string>();
        public static Dictionary<Player, bool> Rainbow = new Dictionary<Player, bool>();
        public static List<Player> Announce = new List<Player>();
        public static Dictionary<Player, Badge> DonatorBadge = new Dictionary<Player, Badge>();
        public static Dictionary<Player, Badge[]> ProprietaryBadge = new Dictionary<Player, Badge[]>();
        public static Dictionary<Player, KeyValuePair<string, int>> MvpMusic = new Dictionary<Player, KeyValuePair<string, int>>();
        public static Page RootPage = null;
        public static List<MenuElement> MyElements = new List<MenuElement>();
        public static GameObject fakePlayer = null;
        public static bool MusicPlayed = false;
        public static CoroutineHandle Coroutine;
        public static DateTime LastReservationUpdateTime = DateTime.Now;

        public static readonly List<string> Colors = new List<string>
            {
                "default", "silver", "nickel", "army_green", "green", "light_green", "emerald", "blue_green", "mint", "lime",
                "yellow", "orange", "brown", "crimson", "carmine", "red", "pumpkin", "pink", "magenta", "dark_pink", "tomato", "cyan", "aqua",
            };

        public static string GetColorHex(string name) => "#" + ReferenceHub.LocalHub.serverRoles.NamedColors.Where(x => x.Name == name).FirstOrDefault()?.ColorHex ?? "FFFFFF";

        public static void SetEnabled(bool b)
        {
            if (b)
            {
                GameplayData.OnInitializingData += GameplayData_OnInitializingData;
                Exiled.Events.Handlers.Player.PreAuthenticating += Player_PreAuthenticating;
                ScoreMvpHandler.MvpCalled += ScoreMvpHandler_MvpCalled;
                Exiled.Events.Handlers.Server.RestartingRound += Server_RestartingRound;
                RgCore.RgCore.RoundStarted += RgCore_RoundStarted;
                Coroutine = Timing.RunCoroutine(Update());
                LoadReservationSlotAsync();
                RegisterMenu(true);
            }
            else
            {
                Timing.KillCoroutines(Coroutine);
                GameplayData.OnInitializingData -= GameplayData_OnInitializingData;
                Exiled.Events.Handlers.Player.PreAuthenticating -= Player_PreAuthenticating;
                Exiled.Events.Handlers.Server.RestartingRound -= Server_RestartingRound;
                RgCore.RgCore.RoundStarted -= RgCore_RoundStarted;
                ScoreMvpHandler.MvpCalled -= ScoreMvpHandler_MvpCalled;
                RegisterMenu(false);
            }
        }

        private static void RgCore_RoundStarted()
        {
            if (fakePlayer != null)
            {
                AudioPlayerBase.AudioPlayers.FirstOrDefault().Value?.Stoptrack(true);
            }
        }

        private static void Server_RestartingRound()
        {
            if (fakePlayer != null)
            {
                AudioPlayerBase.AudioPlayers.Clear();
                NetworkServer.RemovePlayerForConnection(fakePlayer.GetComponent<ReferenceHub>().networkIdentity.connectionToClient, true);
            }
            MusicPlayed = false;
            fakePlayer = null;
            MvpMusic.Clear();
        }

        public static void PlayMusic(string url)
        {
            AudioPlayerBase comp;
            if (fakePlayer == null)
            {
                var npc = UnityEngine.Object.Instantiate(NetworkManager.singleton.playerPrefab);
                var fakeConnection = new FakeConnection(999);
                var hubPlayer = npc.GetComponent<ReferenceHub>();
                NetworkServer.AddPlayerForConnection(fakeConnection, npc);
                comp = npc.AddComponent<AudioPlayerBase>();
                hubPlayer.nicknameSync.Network_myNickSync = $"♬ MVP音乐 ♬";
                hubPlayer.serverRoles.Network_myColor = "crimson";
                hubPlayer.serverRoles.Network_myText = "播放器";
                comp.Owner = hubPlayer;
                comp.AllowUrl = true;
                comp.BroadcastChannel = VoiceChatChannel.RoundSummary;
                comp.Volume = 80f;
                AudioPlayerBase.AudioPlayers.Add(hubPlayer, comp);
                fakePlayer = npc;
            }
            else
                comp = AudioPlayerBase.AudioPlayers.FirstOrDefault().Value;
            comp.Enqueue(url, -1);
            comp.Play(0);
        }

        private static void ScoreMvpHandler_MvpCalled(Player p)
        {
            if (!MvpMusic.ContainsKey(p))
            {
                return;
            }
            Log.Info("播放音乐");
            string url = MvpMusic[p].Key;
            PlayMusic(url);
            string filename = url.Split(url.Contains("http") ? '/' : '\\').Last().Replace(".ogg", "");
            foreach (Player player in Player.List)
                player.SendFormattedMsg("music", "<color=#ff6300>MVP音乐</color>", $"正在播放音乐：<color=yellow>{filename}</color>", 15, "<color=#00FFFF>♬</color>", ScreenLocation.CenterTop, true);
            Log.Info("已启动");
        }

        private static IEnumerator<float> Update()
        {
            while (true)
            {
                try
                {
                    foreach (var player in Rainbow.Where(x => x.Value))
                    {
                        if (player.Key != null)
                        {
                            int index = Colors.FindIndex(p => p == player.Key.RankColor);
                            index++;
                            if (index == Colors.Count)
                            {
                                index = 0;
                            }
                            player.Key.RankColor = Colors[index];
                        }
                    }
                    if (!MusicPlayed && Round.IsLobby && Round.LobbyWaitingTime <= 15 && Round.LobbyWaitingTime > 0)
                    {
                        MusicPlayed = true;
                        var key = MvpMusic.FirstOrDefault(x => x.Value.Value >= 4 && x.Value.Value == MvpMusic.Values.Max(v => v.Value));
                        if (key.Key != null)
                        {
                            string url = key.Value.Key;
                            PlayMusic(url);
                            string filename = url.Split(url.Contains("http") ? '/' : '\\').Last().Replace(".ogg", "");
                            foreach (Player player in Player.List)
                                player.SendFormattedMsg("music", "<color=#ff6300>MVP音乐展示</color>", $"正在播放<color=#00FF00>{key.Key.Nickname}</color>的音乐：<color=yellow>{filename}</color>", 15, "<color=#00FFFF>♬</color>", ScreenLocation.CenterTop, true);
                        }
                        foreach (var other in MvpMusic.Where(x => x.Key != null))
                        {
                            int weight = other.Key == key.Key ? 0 : other.Value.Value + 1;
                            NoResponseCommandRequest cmd =
                                new NoResponseCommandRequest("cdcp_mvp_music", RequestType.Update,
                                new Dictionary<string, object> { { "playback_weight", weight } }, new Dictionary<string, object> { { "userid", other.Key.UserId } });
                            _ = cmd.ProcessNonQuery();
                        }
                    }
                    if (LastReservationUpdateTime.AddHours(1) < DateTime.Now)
                    {
                        Log.Info("更新预留位");
                        LastReservationUpdateTime = DateTime.Now;
                        LoadReservationSlotAsync();
                    }
                }
                catch (Exception e)
                {
                    Log.Error(e);
                }
                yield return Timing.WaitForSeconds(0.3f);
            }
        }

        private static void Player_PreAuthenticating(Exiled.Events.EventArgs.Player.PreAuthenticatingEventArgs ev)
        {
            if (ev.Flags.HasFlag(CentralAuthPreauthFlags.NorthwoodStaff))
                return;
            bool admin = (SponsorPlugin.instance.Config as SponsorConfig).AdminReservationEnabled;
            int vipcount = Player.List.Count(x => Reservalist.Contains(x.UserId));
            int playercount = NetworkServer.connections.Count - 1 - (!Player.List.Any(x => x.RemoteAdminAccess) ? 0 : 1);
            int slot = (SponsorPlugin.instance.Config as SponsorConfig).ReservedSlot;
            int maxplayer = CustomNetworkManager.slots - slot - (admin ? 1 : 0);
            bool isadmin = ServerStatic.PermissionsHandler.GetUserGroup(ev.UserId) != null;
            if (admin && isadmin && !Player.List.Any(x => x.RemoteAdminAccess))
            {
                return;
            }
            if (!Reservalist.Contains(ev.UserId))
            {
                if (playercount - Math.Min(vipcount, slot) >= maxplayer)
                {
                    ev.Reject("当前服务器普通位已满，您可以购买预留位来获得满人进服权限\n购买链接在群公告\n", true);
                    return;
                }
            }
        }

        private static async void LoadReservationSlotAsync()
        {
            if (DatabaseController.DatabaseDisabled)
                return;
            Reservalist.Clear();
            QueryCommandRequest data = new QueryCommandRequest("cdcp_reversed_slot", null, false, "userid", "expires");
            List<Dictionary<string, object>> datas = await data.GetResponseResult();
            foreach (var key in datas)
            {
                string userid = (string)key["userid"];
                DateTime expires = DateTime.Parse(key["expires"].ToString());
                if (expires < DateTime.Now && expires.Year > 2018)
                {
                    NoResponseCommandRequest cmd = new NoResponseCommandRequest("cdcp_reversed_slot", RequestType.Delete, null, new Dictionary<string, object>() { { "userid", userid } });
                    _ = cmd.ProcessNonQuery();
                }
                else
                {
                    Reservalist.Add(userid);
                }
            }
        }

        private static async void GameplayData_OnInitializingData(Player p)
        {
            if (DatabaseController.DatabaseDisabled)
                return;
            var filter = new Dictionary<string, object> { { "userid", p.UserId } };

            #region 赞助者称号

            QueryCommandRequest data = new QueryCommandRequest("cdcp_badge", filter, false, "badge", "color", "expires");
            List<Dictionary<string, object>> datas = await data.GetResponseResult();
            if (datas.Count > 0)
            {
                string badge = (string)datas.First()["badge"];
                string color = (string)datas.First()["color"];
                DateTime expires = DateTime.Parse(datas.First()["expires"].ToString());
                if (expires < DateTime.Now && expires.Year > 2018)
                {
                    NoResponseCommandRequest cmd2 = new NoResponseCommandRequest("cdcp_badge", RequestType.Delete, null, filter);
                    _ = cmd2.ProcessNonQuery();
                    p.SendFormattedMsg("donator", "<color=#FF4500>赞助项目</color>", "你的 [<color=yellow>赞助称号</color>] 已过期", 5, MsgType.Notification, ScreenLocation.CenterTop, false);
                }
                else
                {
                    DonatorBadge.Remove(p);
                    DonatorBadge.Add(p, new Badge(badge, color, "赞助者称号", expires));
                }
            }

            #endregion 赞助者称号

            #region 专属称号

            QueryCommandRequest data1 = new QueryCommandRequest("cdcp_badge_special", filter, false, "badge", "color", "description");
            List<Dictionary<string, object>> datas1 = await data1.GetResponseResult();
            if (datas1.Count > 0)
            {
                List<Badge> badges = new List<Badge>();
                foreach (var item in datas1)
                {
                    string badge = (string)item["badge"];
                    string color = (string)item["color"];
                    string description = (string)item["description"];
                    badges.Add(new Badge(badge, color, description, DateTime.MinValue));
                }
                if (badges.Count > 0)
                {
                    ProprietaryBadge.Remove(p);
                    ProprietaryBadge.Add(p, badges.ToArray());
                }
            }

            #endregion 专属称号

            #region 彩虹称号

            QueryCommandRequest data3 = new QueryCommandRequest("cdcp_rainbow", filter, false, "expires");
            List<Dictionary<string, object>> datas3 = await data3.GetResponseResult();
            if (datas3.Count > 0)
            {
                DateTime expires = DateTime.Parse(datas3.First()["expires"].ToString());
                if (expires < DateTime.Now && expires.Year > 2018)
                {
                    NoResponseCommandRequest cmd2 = new NoResponseCommandRequest("cdcp_rainbow", RequestType.Delete, null, filter);
                    _ = cmd2.ProcessNonQuery();
                    p.SendFormattedMsg("donator", "<color=#FF4500>赞助项目</color>", "你的 [<color=yellow>彩虹称号</color>] 已过期", 5, MsgType.Notification, ScreenLocation.CenterTop, false);
                }
                else
                {
                    Rainbow.Remove(p);
                    Rainbow.Add(p, true);
                    Announce.Add(p);
                }
            }

            #endregion 彩虹称号

            #region 自定义入服消息

            QueryCommandRequest data4 = new QueryCommandRequest("cdcp_custom_message", filter, false, "content", "expires");
            List<Dictionary<string, object>> datas4 = await data4.GetResponseResult();
            if (datas4.Count > 0)
            {
                string content = (string)datas4.First()["content"];
                DateTime expires = DateTime.Parse(datas4.First()["expires"].ToString());
                if (expires < DateTime.Now && expires.Year > 2018)
                {
                    NoResponseCommandRequest cmd2 = new NoResponseCommandRequest("cdcp_custom_message", RequestType.Delete, null, filter);
                    _ = cmd2.ProcessNonQuery();
                    p.SendFormattedMsg("donator", "<color=#FF4500>赞助项目</color>", "你的 [<color=yellow>自定义入服消息</color>] 已过期", 5, MsgType.Notification, ScreenLocation.CenterTop, false);
                }
                else
                {
                    HudHandler.SendFormattedMsg("custom_joinmsg", "<color=#4876FF>入服消息</color>",
                        $"<color=#00FF00>{p.Nickname}</color> {content}加入了服务器！", 5, MsgType.Announcement, ScreenLocation.CenterTop,
                        true, Player.List.ToArray());
                }
            }

            #endregion 自定义入服消息

            #region MVP音乐

            QueryCommandRequest data5 = new QueryCommandRequest("cdcp_mvp_music", filter, false, "expires", "url", "playback_weight");
            List<Dictionary<string, object>> datas5 = await data5.GetResponseResult();
            if (datas5.Count > 0)
            {
                string url = (string)datas5.First()["url"];
                int weight = int.Parse(datas5.First()["playback_weight"].ToString());
                DateTime expires = DateTime.Parse(datas5.First()["expires"].ToString());
                if (expires < DateTime.Now && expires.Year > 2018)
                {
                    NoResponseCommandRequest cmd2 = new NoResponseCommandRequest("cdcp_mvp_music", RequestType.Delete, null, filter);
                    _ = cmd2.ProcessNonQuery();
                    p.SendFormattedMsg("donator", "<color=#FF4500>赞助项目</color>", "你的 [<color=yellow>MVP音乐</color>] 已过期", 5, MsgType.Notification, ScreenLocation.CenterTop, false);
                }
                else
                {
                    Log.Info("音乐：" + url);
                    MvpMusic.Remove(p);
                    MvpMusic.Add(p, new KeyValuePair<string, int>(url, weight));
                }
            }

            #endregion MVP音乐

            var datax = GameplayData.GameDatas.FirstOrDefault(x => x.Player == p);
            if (datax.BadgePreference == 1)
            {
                if (DonatorBadge.ContainsKey(p))
                {
                    p.ReferenceHub.serverRoles.Network_myText = DonatorBadge[p].Name;
                    p.ReferenceHub.serverRoles.Network_myColor = DonatorBadge[p].Color;
                }
                else
                    datax.BadgePreference = 0;
            }
            else if (datax.BadgePreference > 1)
            {
                if (ProprietaryBadge.ContainsKey(p) && ProprietaryBadge[p].Length >= datax.BadgePreference - 1)
                {
                    var badge = ProprietaryBadge[p][datax.BadgePreference - 2];
                    p.ReferenceHub.serverRoles.Network_myText = badge.Name;
                    p.ReferenceHub.serverRoles.Network_myColor = badge.Color;
                }
                else
                    datax.BadgePreference = 0;
            }
            if (datax.BadgePreference == 0)
            {
                if (DonatorBadge.ContainsKey(p))
                {
                    p.ReferenceHub.serverRoles.Network_myText = DonatorBadge[p].Name;
                    p.ReferenceHub.serverRoles.Network_myColor = DonatorBadge[p].Color;
                }
                else if (ProprietaryBadge.ContainsKey(p))
                {
                    var badge = ProprietaryBadge[p].FirstOrDefault();
                    p.ReferenceHub.serverRoles.Network_myText = badge.Name;
                    p.ReferenceHub.serverRoles.Network_myColor = badge.Color;
                }
            }
        }

        public static void RegisterMenu(bool enable)
        {
            if (enable)
            {
                RootPage = new Page(MenuInterface.Root, "PersonalBadges", "个人称号", null, null, Elements, false);
                MenuInterface.RegisteredElements.Add(RootPage);
            }
            else
            {
                MenuInterface.RegisteredElements.Remove(RootPage);
            }
        }

        private static MenuElement[] Elements(Player player)
        {
            List<FunctionalElement> list = new List<FunctionalElement>()
            {
                new Page(RootPage, "DonatorBadge", "赞助称号", (t)=> BadgeDisplay(t, 0), DonatorBadgeAvailable, (p) =>
                {
                    var badge = DonatorBadge[p];
                    return new MenuElement[]
                    {
                        new Text(null, "Text", ()=>$"<b>称号：[<color={GetColorHex(badge.Color)}>{badge.Name}</color>]</b>", false),
                        new Text(null, "Text", ()=>$"<b>过期时间：<color=yellow>{badge.Expires:yyyy-MM-dd}</color></b>", false),
                        new Option(null, "ApplyDonatorBadge", "应用此称号", null, null, (t) => ApplyBadge(t,0), false)
                    };
                }, false),
                new Option(RootPage, "SwitchRainbow", "彩色称号", RainbowDisplay, null, SwitchRainbow, false),
            };
            if (ProprietaryBadge.ContainsKey(player))
            {
                for (int i = 0; i < ProprietaryBadge[player].Length; i++)
                {
                    int j = i + 1;
                    list.Add(new Page(RootPage, $"SpecialBadge_{j}", $"专属称号#{j}", (p) => BadgeDisplay(p, j), null, (p) =>
                    {
                        var badge = GetBadge(p, j);
                        return new MenuElement[]
                        {
                        new Text(null, "Text", ()=>$"称号：[<color={GetColorHex(badge.Color)}>{badge.Name}</color>]", false),
                        new Text(null, "Text", ()=>$"描述：{badge.Description}", false),
                        new Option(null, $"ApplySpecialBadge_{j}", "应用此称号", null, null, (t) => ApplyBadge(t,j), false)
                        };
                    }, false));
                }
            }
            return list.ToArray();
        }

        private static string SwitchRainbow(Player player)
        {
            if (Rainbow.ContainsKey(player))
            {
                Rainbow[player] = !Rainbow[player];
            }
            return "";
        }

        private static string RainbowDisplay(Player player)
        {
            string status = "<color=#AA0000>未购买</color>";
            if (Rainbow.ContainsKey(player))
            {
                status = Rainbow[player] ? "<color=#00FF00>启用</color>" : "<color=#FF0000>禁用</color>";
            }
            return $"彩虹模式：{status}";
        }

        private static string ApplyBadge(Player player, int index)
        {
            Badge badge = GetBadge(player, index);
            if (badge.Name.IsEmpty())
            {
                return "装备失败，很奇怪的问题呢。。。";
            }
            player.RankName = badge.Name;
            player.RankColor = badge.Color;
            GameplayData.GameDatas.FirstOrDefault(x => x.Player == player).BadgePreference = (byte)(index + 1);
            return $"已装备 [<color={GetColorHex(badge.Color)}>{badge.Name}</color>]";
        }

        private static bool DonatorBadgeAvailable(Player player, out string response)
        {
            response = "";
            return DonatorBadge.ContainsKey(player);
        }

        private static Badge GetBadge(Player player, int index)
        {
            Badge badge = default;
            if (index == 0 && DonatorBadge.ContainsKey(player))
            {
                badge = DonatorBadge[player];
            }
            else if (index > 0 && ProprietaryBadge.ContainsKey(player) && index - 1 < ProprietaryBadge[player].Length)
            {
                badge = ProprietaryBadge[player][index - 1];
            }
            return badge;
        }

        private static string BadgeDisplay(Player player, int index)
        {
            Badge badge;
            if (index == 0 && DonatorBadge.ContainsKey(player))
            {
                badge = DonatorBadge[player];
            }
            else if (index > 0 && ProprietaryBadge.ContainsKey(player) && index - 1 < ProprietaryBadge[player].Length)
            {
                badge = ProprietaryBadge[player][index - 1];
            }
            else
            {
                return $"<color=#696969>-[无称号]-</color>";
            }
            return $"-[<color={GetColorHex(badge.Color)}>{badge.Name}</color>]-";
        }
    }
}